////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   terraintexturepainter.cpp
//  Version:     v1.00
//  Created:     8/10/2002 by Timur.
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////


#include "StdAfx.h"
#include "TerrainTexturePainter.h"
#include "Viewport.h"
#include ".\Terrain\Heightmap.h"
#include ".\Terrain\TerrainGrid.h"
#include "VegetationMap.h"
#include "Objects\DisplayContext.h"

#include "TerrainPainterPanel.h"

#include "CryEditDoc.h"
#include ".\Terrain\Layer.h"

#include "Util\ImagePainter.h"

#include <I3DEngine.h>

#include "Console\ConsoleSync.h"

#include "Terrain/Heightmap.h"
#include "Terrain/TerrainManager.h"
#include "Terrain/SurfaceType.h"

struct CUndoTPSector
{
	CUndoTPSector()
	{
		m_pImg = 0;
		m_pImgRedo = 0;
	}

	CPoint tile;
	int x, y;
	int w;
	uint32 dwSize;
	CImage * m_pImg;
	CImage * m_pImgRedo;
};


struct CUndoTPLayerIdSector
{
	CUndoTPLayerIdSector()
	{
		m_pImg = 0;
		m_pImgRedo = 0;
	}

	int x, y;
	CByteImage * m_pImg;
	CByteImage * m_pImgRedo;
};

struct CUndoTPElement
{
	std::vector <CUndoTPSector> sects;
	std::vector <CUndoTPLayerIdSector> layerIds;

	~CUndoTPElement()
	{
		Clear();
	}

	void AddSector(float fpx, float fpy, float radius )
	{
		CHeightmap * heightmap = GetIEditor()->GetHeightmap();

		uint32 dwMaxRes = heightmap->m_TerrainRGBTexture.CalcMaxLocalResolution(0,0,1,1);

		uint32 dwTileCountX = heightmap->m_TerrainRGBTexture.GetTileCountX();
		uint32 dwTileCountY = heightmap->m_TerrainRGBTexture.GetTileCountY();

		float	gx1 = fpx-radius-2.0f/dwMaxRes;
		float	gx2 = fpx+radius+2.0f/dwMaxRes;
		float	gy1 = fpy-radius-2.0f/dwMaxRes;
		float gy2 = fpy+radius+2.0f/dwMaxRes;

		if(gx1<0.0f) gx1 = 0.0f;
		if(gy1<0.0f) gy1 = 0.0f;
		if(gx2>1.0f) gx2 = 1.0f;
		if(gy2>1.0f) gy2 = 1.0f;

		CRect recTiles=CRect(
			(uint32)floor(gx1 * dwTileCountX),
			(uint32)floor(gy1 * dwTileCountY),
			(uint32)ceil( gx2 * dwTileCountX),
			(uint32)ceil( gy2 * dwTileCountY));

		for(uint32 dwTileY=recTiles.top;dwTileY<recTiles.bottom;++dwTileY)
			for(uint32 dwTileX=recTiles.left;dwTileX<recTiles.right;++dwTileX)
			{
				uint32 dwLocalSize = heightmap->m_TerrainRGBTexture.GetTileResolution(dwTileX,dwTileY);
				uint32 dwSize = heightmap->m_TerrainRGBTexture.GetTileResolution(dwTileX,dwTileY) * heightmap->m_TerrainRGBTexture.GetTileCountX();

				uint32 gwid = dwSize * radius * 4;
				if(gwid<32)
					gwid = 32;
				else if(gwid<64)
					gwid = 64;
				else if(gwid<128)
					gwid = 128;
				else
					gwid = 256;

				float	x1 = gx1;
				float	x2 = gx2;
				float	y1 = gy1;
				float y2 = gy2;
				
				if( x1 < (float)dwTileX / dwTileCountX ) x1 = (float)dwTileX / dwTileCountX;
				if( y1 < (float)dwTileY / dwTileCountY ) y1 = (float)dwTileY / dwTileCountY;
				if( x2 > (float)(dwTileX + 1) / dwTileCountX ) x2 = (float)(dwTileX+1) / dwTileCountX;
				if( y2 > (float)(dwTileY + 1) / dwTileCountY ) y2 = (float)(dwTileY+1) / dwTileCountY;

				uint32 wid = gwid;

				if(wid>dwLocalSize)
					wid = dwLocalSize;

				CRect recSects=CRect(
					((int)floor(x1 * dwSize / wid)) * wid,
					((int)floor(y1 * dwSize / wid)) * wid,
					((int)ceil(x2 * dwSize / wid)) * wid,
					((int)ceil(y2 * dwSize / wid)) * wid);


				for(uint32 sy = recSects.top; sy < recSects.bottom; sy+=wid)
					for(uint32 sx = recSects.left; sx < recSects.right; sx+=wid)
					{
						bool bFind = false;
						for (int i=sects.size()-1; i>=0; i--)
						{
							CUndoTPSector * pSect = &sects[i];
							if(pSect->tile.x==dwTileX && pSect->tile.y==dwTileY)
							{
								if(pSect->x == sx &&  pSect->y == sy)
								{
									bFind = true;
									break;
								}
							}
						}
						if(!bFind)
						{
							CUndoTPSector newSect;
							newSect.x = sx;
							newSect.y = sy;
							newSect.w = wid;
							newSect.tile.x = dwTileX;
							newSect.tile.y = dwTileY;
							newSect.dwSize = dwSize;

							newSect.m_pImg = new CImage();
							newSect.m_pImg->Allocate(newSect.w, newSect.w);

							CUndoTPSector * pSect = &newSect;

							heightmap->m_TerrainRGBTexture.GetSubImageStretched((float)pSect->x / pSect->dwSize, (float)pSect->y / pSect->dwSize, 
								(float)(pSect->x + pSect->w) / pSect->dwSize, (float)(pSect->y + pSect->w) / pSect->dwSize, *pSect->m_pImg);

							sects.push_back(newSect);
						}
					}
			}

		// Store LayerIDs
		const uint32 layerSize = 64;

		CRect reclids=CRect(
			(uint32)floor(gx1 * heightmap->GetWidth() / layerSize),
			(uint32)floor(gy1 * heightmap->GetHeight()/ layerSize),
			(uint32)ceil( gx2 * heightmap->GetWidth() / layerSize),
			(uint32)ceil( gy2 * heightmap->GetHeight()/ layerSize));

		for(uint32 ly = reclids.top; ly < reclids.bottom; ly++)
			for(uint32 lx = reclids.left; lx < reclids.right; lx++)
			{
				bool bFind = false;
				for (int i=layerIds.size()-1; i>=0; i--)
				{
					CUndoTPLayerIdSector * pSect = &layerIds[i];
					if(pSect->x == lx &&  pSect->y == ly)
					{
						bFind = true;
						break;
					}
				}
				if(!bFind)
				{
					CUndoTPLayerIdSector newSect;
					CUndoTPLayerIdSector * pSect = &newSect;

					pSect->m_pImg = new CByteImage;
					pSect->x = lx;
					pSect->y = ly;
					heightmap->GetLayerIdBlock(pSect->x*layerSize, pSect->y*layerSize, layerSize, layerSize, *pSect->m_pImg);

					layerIds.push_back(newSect);
				}
			}
	}
	
	void Paste(bool bIsRedo = false)
	{
		CHeightmap * heightmap = GetIEditor()->GetHeightmap();

		bool bFirst = true;
		CPoint gminp;
		CPoint gmaxp;

		AABB aabb(AABB::RESET);

		for (int i=sects.size()-1; i>=0; i--)
		{
			CUndoTPSector * pSect = &sects[i];
			heightmap->m_TerrainRGBTexture.SetSubImageStretched((float)pSect->x / pSect->dwSize, (float)pSect->y / pSect->dwSize, 
				(float)(pSect->x + pSect->w) / pSect->dwSize, (float)(pSect->y + pSect->w) / pSect->dwSize, *(bIsRedo ? pSect->m_pImgRedo : pSect->m_pImg) );

			aabb.Add(	Vec3((float)pSect->x / pSect->dwSize, (float)pSect->y / pSect->dwSize, 0));
			aabb.Add(	Vec3((float)(pSect->x + pSect->w) / pSect->dwSize, (float)(pSect->y + pSect->w) / pSect->dwSize, 0));
		}

		// LayerIDs
		for (int i=layerIds.size()-1; i>=0; i--)
		{
			CUndoTPLayerIdSector * pSect = &layerIds[i];
			heightmap->SetLayerIdBlock(pSect->x*pSect->m_pImg->GetWidth(), pSect->y*pSect->m_pImg->GetHeight(), *(bIsRedo ? pSect->m_pImgRedo : pSect->m_pImg) );
			heightmap->UpdateEngineTerrain( pSect->x*pSect->m_pImg->GetWidth(), pSect->y*pSect->m_pImg->GetHeight(), pSect->m_pImg->GetWidth(),pSect->m_pImg->GetHeight(),true, false );
		}

		if(!aabb.IsReset())
		{
			RECT rc = {	aabb.min.x * heightmap->GetWidth(), aabb.min.y * heightmap->GetHeight(), 
									aabb.max.x * heightmap->GetWidth(), aabb.max.y * heightmap->GetHeight()};
			heightmap->UpdateLayerTexture(rc);
		}
	}

	void StoreRedo()
	{
		CHeightmap * heightmap = GetIEditor()->GetHeightmap();

		for (int i=sects.size()-1; i>=0; i--)
		{
			CUndoTPSector * pSect = &sects[i];
			if(!pSect->m_pImgRedo)
			{
				pSect->m_pImgRedo = new CImage();
				pSect->m_pImgRedo->Allocate(pSect->w, pSect->w);

				heightmap->m_TerrainRGBTexture.GetSubImageStretched((float)pSect->x / pSect->dwSize, (float)pSect->y / pSect->dwSize, 
							(float)(pSect->x + pSect->w) / pSect->dwSize, (float)(pSect->y + pSect->w) / pSect->dwSize, *pSect->m_pImgRedo);
			}
		}

		// LayerIds
		for (int i=layerIds.size()-1; i>=0; i--)
		{
			CUndoTPLayerIdSector * pSect = &layerIds[i];
			if(!pSect->m_pImgRedo)
			{
				pSect->m_pImgRedo = new CByteImage();
				heightmap->GetLayerIdBlock(pSect->x*pSect->m_pImg->GetWidth(), pSect->y*pSect->m_pImg->GetHeight(), pSect->m_pImg->GetWidth(), pSect->m_pImg->GetHeight(), *pSect->m_pImgRedo);
			}
		}
	}

	void Clear()
	{
		for (int i=0; i<sects.size(); i++)
		{
			CUndoTPSector * pSect = &sects[i];
			delete pSect->m_pImg;
			pSect->m_pImg = 0;

			delete pSect->m_pImgRedo;
			pSect->m_pImgRedo = 0;
		}

		for (int i=0; i<layerIds.size(); i++)
		{
			CUndoTPLayerIdSector * pSect = &layerIds[i];
			delete pSect->m_pImg;
			pSect->m_pImg = 0;

			delete pSect->m_pImgRedo;
			pSect->m_pImgRedo = 0;
		}
	}

	int GetSize()
	{
		int size = 0;
		for (int i=0; i<sects.size(); i++)
		{
			CUndoTPSector * pSect = &sects[i];
			if(pSect->m_pImg)
				size +=pSect->m_pImg->GetSize();
			if(pSect->m_pImgRedo)
				size +=pSect->m_pImgRedo->GetSize();
		}
		for (int i=0; i<layerIds.size(); i++)
		{
			CUndoTPLayerIdSector * pSect = &layerIds[i];
			if(pSect->m_pImg)
				size +=pSect->m_pImg->GetSize();
			if(pSect->m_pImgRedo)
				size +=pSect->m_pImgRedo->GetSize();
		}
		return size;
	}
};

//////////////////////////////////////////////////////////////////////////
//! Undo object.
class CUndoTexturePainter : public IUndoObject
{
public:
	CUndoTexturePainter( CTerrainTexturePainter * pTool)
	{
		m_pUndo = pTool->m_pTPElem;
		pTool->m_pTPElem = 0;
	}

protected:
	virtual void Release() 
	{ 
		delete m_pUndo;
		delete this; 
	};

	virtual int GetSize() {	return sizeof(*this) + (m_pUndo ? m_pUndo->GetSize() : 0) ; };
	virtual const char* GetDescription() { return "Terrain Painter Modify"; };

	virtual void Undo( bool bUndo )
	{
		if (bUndo)
			m_pUndo->StoreRedo();

		if(m_pUndo)
			m_pUndo->Paste();
	}
	virtual void Redo()
	{
		if(m_pUndo)
			m_pUndo->Paste(true);
	}

private:
	CUndoTPElement * m_pUndo;
};


//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(CTerrainTexturePainter,CEditTool)

CTextureBrush CTerrainTexturePainter::m_brush;

namespace {
	int s_toolPanelId = 0;
	CTerrainPainterPanel *s_toolPanel = 0;
};

//////////////////////////////////////////////////////////////////////////
CTerrainTexturePainter::CTerrainTexturePainter()
{
	m_brush.maxRadius=1000.0f;

	SetStatusText( _T("Paint Texture Layers") );

	m_heightmap = GetIEditor()->GetHeightmap();
	assert( m_heightmap );

	m_3DEngine = GetIEditor()->Get3DEngine();
	assert( m_3DEngine );

	m_renderer = GetIEditor()->GetRenderer();
	assert( m_renderer );

	m_terrainGrid = m_heightmap->GetTerrainGrid();
	assert(m_terrainGrid);
	
	m_pointerPos(0,0,0);
	GetIEditor()->ClearSelection();

	//////////////////////////////////////////////////////////////////////////
	// Initialize sectors.
	//////////////////////////////////////////////////////////////////////////
	SSectorInfo sectorInfo;
	m_heightmap->GetSectorsInfo( sectorInfo );
	m_numSectors = sectorInfo.numSectors;
	m_sectorSize = sectorInfo.sectorSize;
	m_sectorTexSize = sectorInfo.sectorTexSize;
	m_surfaceTextureSize = sectorInfo.surfaceTextureSize;

//	m_pixelsPerMeter = ((float)sectorInfo.sectorTexSize) / sectorInfo.sectorSize;

	// Initialize terrain texture generator.
	//m_terrTexGen.PrepareHeightmap( m_surfaceTextureSize,m_surfaceTextureSize );
	//m_terrTexGen.PrepareLayers( m_surfaceTextureSize,m_surfaceTextureSize );

	m_bIsPainting = false;
	m_pTPElem = 0;
}

//////////////////////////////////////////////////////////////////////////
CTerrainTexturePainter::~CTerrainTexturePainter()
{
	m_pointerPos(0,0,0);

	if (GetIEditor()->IsUndoRecording())
		GetIEditor()->CancelUndo();
}

//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::BeginEditParams( IEditor *ie,int flags )
{
	if (!s_toolPanelId)
	{
		s_toolPanel = new CTerrainPainterPanel(this,AfxGetMainWnd());
		s_toolPanelId = GetIEditor()->AddRollUpPage( ROLLUP_TERRAIN,_T("Layer Painter"),s_toolPanel );
		AfxGetMainWnd()->SetFocus();

		s_toolPanel->SetBrush(m_brush);
	}
}

//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::EndEditParams()
{
	if (s_toolPanelId)
	{
		GetIEditor()->RemoveRollUpPage(ROLLUP_TERRAIN,s_toolPanelId);
		s_toolPanel = 0;
		s_toolPanelId = 0;
	}
}

//////////////////////////////////////////////////////////////////////////
bool CTerrainTexturePainter::MouseCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
{
	bool bPainting = false;
	m_pointerPos = view->ViewToWorld( point,0,true );

	m_brush.bErase = false;

	if(m_bIsPainting)
	{
		if(event == eMouseLDown || event == eMouseLUp)
		{
			Action_StopUndo();
		}
	}

	if((flags & MK_CONTROL)!=0)															// pick layerid
	{
		if(event == eMouseLDown)
			Action_PickLayerId();
	}
	else if(event == eMouseLDown || (event == eMouseMove && (flags&MK_LBUTTON)) )		// paint
		Action_Paint();
	
	GetIEditor()->SetStatusText( _T("L-Mouse: Paint   +/-: Change Brush Radius   CTRL L-Mouse: Pick LayerId") );

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::Display( DisplayContext &dc )
{
	dc.SetColor( 0,1,0,1 );

	if(m_pointerPos.x==0 && m_pointerPos.y==0 && m_pointerPos.z==0)
		return;		// mouse cursor not in window

	dc.DepthTestOff();
	dc.DrawTerrainCircle( m_pointerPos,m_brush.radius,0.2f );
	dc.DepthTestOn();
}

//////////////////////////////////////////////////////////////////////////
bool CTerrainTexturePainter::OnKeyDown( CViewport *view,uint32 nChar,uint32 nRepCnt,uint32 nFlags )
{
	bool bProcessed = false;

	if (nChar == VK_ADD)
	{
		m_brush.radius += 1;
		bProcessed = true;
	}
	if (nChar == VK_SUBTRACT)
	{
		if (m_brush.radius > 1)
			m_brush.radius -= 1;
		bProcessed = true;
	}
	if (bProcessed && s_toolPanel)
	{
		//s_toolPanel->SetBrush(m_brush);
	}
	if (m_brush.radius < m_brush.minRadius)
		m_brush.radius = m_brush.minRadius;
	if (m_brush.radius > m_brush.maxRadius)
		m_brush.radius = m_brush.maxRadius;

	if (bProcessed)
	{
		if (s_toolPanel)
			s_toolPanel->SetBrush(m_brush);
	}

	return bProcessed;
}


//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::Action_PickLayerId()
{
	int iTerrainSize = m_3DEngine->GetTerrainSize();																			// in m
	int hmapWidth = m_heightmap->GetWidth();
	int hmapHeight = m_heightmap->GetHeight();

	int iX = (int)((m_pointerPos.y*hmapWidth)/iTerrainSize);			// maybe +0.5f is needed
	int iY = (int)((m_pointerPos.x*hmapHeight)/iTerrainSize);			// maybe +0.5f is needed

	if(iX>=0 && iX<iTerrainSize)
	if(iY>=0 && iY<iTerrainSize)
	{	
		uint32 dwLayerid = m_heightmap->GetLayerIdAt(iX,iY);

		CCryEditDoc *pDoc = GetIEditor()->GetDocument();
		CLayer *pLayer = CTerrainManager::GetTerrainManager().FindLayerByLayerId(dwLayerid);

		s_toolPanel->SelectLayer(pLayer);
	}
}

//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::Action_FixMaterialCount()
{
	CCryEditDoc *pDoc = GetIEditor()->GetDocument();
	if (!pDoc)
		return;

	// Find the boundaries of the engine terrain sector:
	int nSectorSize = m_3DEngine->GetTerrainSectorSize();

	int nTerrainSize = m_3DEngine->GetTerrainSize();	// in m
	int hmapWidth = m_heightmap->GetWidth();
	int hmapHeight = m_heightmap->GetHeight();

	int iX = (int)((m_pointerPos.y * hmapWidth) / nTerrainSize);
	int iY = (int)((m_pointerPos.x * hmapHeight) / nTerrainSize);

	int iMinX = (iX / nSectorSize) * nSectorSize;
	int iMinY = (iY / nSectorSize) * nSectorSize;
	int iMaxX = iMinX + nSectorSize;
	int iMaxY = iMinY + nSectorSize;

	int UsageCounters[MAX_SURFACE_TYPE_ID_COUNT + 1];
	int iUsedSurfaceIDs = 0;
	ZeroMemory(UsageCounters, sizeof(UsageCounters));
	for (int y = iMinY; y <= iMaxY; y++)
		for (int x = iMinX; x <= iMaxX; x++)
			if (!(UsageCounters[m_heightmap->GetLayerIdAt(x, y)]++)) iUsedSurfaceIDs++;

	// The layer currently selected in the GUI won't be removed even if it is the least used one.
	CLayer *pSelectedLayer = GetSelectedLayer();
	int iSelected = pSelectedLayer? pSelectedLayer->GetCurrentLayerId(): -1;

	// While the layers are too many, we take the least used one and overwrite it with the most used one.
	while (iUsedSurfaceIDs >= 15)
	{
		int iLeastUsed = -1;
		int iMostUsed = -1;

		for (int i = 0; i < MAX_SURFACE_TYPE_ID_COUNT + 1; i++)
			if ((iLeastUsed < 0 || UsageCounters[i] && UsageCounters[i] < UsageCounters[iLeastUsed]) && i != iSelected)
				iLeastUsed = i;

		for (int i = 0; i < MAX_SURFACE_TYPE_ID_COUNT + 1; i++)
			if (iMostUsed < 0 || i != iLeastUsed && UsageCounters[i] > UsageCounters[iMostUsed])
				iMostUsed = i;

		for (int y = iMinY; y <= iMaxY; y++)
			for (int x = iMinX; x <= iMaxX; x++)
				if (m_heightmap->GetLayerIdAt(x, y) == iLeastUsed)
					m_heightmap->SetLayerIdAt(x, y, iMostUsed);

		UsageCounters[iMostUsed] += UsageCounters[iLeastUsed];
		UsageCounters[iLeastUsed] = 0;

		iUsedSurfaceIDs--;
	}

	m_heightmap->UpdateEngineTerrain(iMinX, iMinY, nSectorSize - 1, nSectorSize - 1, false, true);
}
//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::Action_Paint()
{
	CCryEditDoc *pDoc = GetIEditor()->GetDocument();

	//////////////////////////////////////////////////////////////////////////
	// Paint spot on selected layer.
	//////////////////////////////////////////////////////////////////////////
	CLayer *pLayer = GetSelectedLayer();
	if (!pLayer)
		return;

	////////////////////////////////////////////////////////////////////////////////////
	// Before everything else, check if the action would cause sector palette problems.
	////////////////////////////////////////////////////////////////////////////////////
	if (m_brush.bDetailLayer && pLayer->GetSurfaceType())
	{
		int nTerrainSizeInUnits = m_3DEngine->GetTerrainSize();
		int x = RoundFloatToInt(m_pointerPos.y * (float)m_heightmap->GetWidth() / nTerrainSizeInUnits);
		int y = RoundFloatToInt(m_pointerPos.x * (float)m_heightmap->GetHeight() / nTerrainSizeInUnits);

		int unitSize = m_heightmap->GetUnitSize();
		int r = RoundFloatToInt(m_brush.radius / unitSize);

		if (!GetIEditor()->Get3DEngine()->GetITerrain()->CanPaintSurfaceType(x, y, r, pLayer->GetEngineSurfaceTypeId()))
			return;
	}

	if(!CTerrainManager::GetTerrainManager().IsNewTerranTextureSystem())
		if(pLayer->IsAutoGen())								// old system was not using some layers
			return;

	static bool bPaintLock = false;
	if (bPaintLock)
		return;

	bPaintLock = true;
	
	float fTerrainSize = (float)m_3DEngine->GetTerrainSize();																			// in m

	SEditorPaintBrush br(CTerrainManager::GetTerrainManager().GetHeightmap(),*pLayer,m_brush.bMaskByLayerSettings,m_brush.m_dwMaskLayerId);

	br.m_cFilterColor = m_brush.m_cFilterColor*m_brush.m_fBrightness;
	br.fRadius = m_brush.radius/fTerrainSize;
	br.hardness = m_brush.hardness;
	br.color = m_brush.value;
	if (m_brush.bErase)
		br.color = 0;

	// Paint spot on layer mask.
	{
		float fX=m_pointerPos.y/fTerrainSize;							// 0..1
		float fY=m_pointerPos.x/fTerrainSize;							// 0..1

		// change terrain texture
		{
			CRGBLayer &TerrainRGBLayer = CTerrainManager::GetTerrainManager().GetHeightmap().m_TerrainRGBTexture;

			assert(TerrainRGBLayer.CalcMinRequiredTextureExtend());

			// load m_texture is needed/possible
			pLayer->PrecacheTexture();

			if(pLayer->m_texture.IsValid())
			{
				Action_CollectUndo(fX, fY, br.fRadius);
				TerrainRGBLayer.PaintBrushWithPatternTiled(fX,fY,br,pLayer->m_texture);
			}
		}

		if(m_brush.bDetailLayer)		// we also want to change the detail layer data
		{
			// get unique layerId
			uint32 dwLayerId = pLayer->GetOrRequestLayerId();					assert(dwLayerId!=0xffffffff);	

			m_heightmap->PaintLayerId(fX,fY,br,dwLayerId);
		}
	}


	Vec3 vMin = m_pointerPos-Vec3(m_brush.radius,m_brush.radius,0);
	Vec3 vMax = m_pointerPos+Vec3(m_brush.radius,m_brush.radius,0);

	//////////////////////////////////////////////////////////////////////////
	// Update Terrain textures.
	//////////////////////////////////////////////////////////////////////////
	{
		int iTerrainSize = m_3DEngine->GetTerrainSize();												// in meters
		int iTexSectorSize = m_3DEngine->GetTerrainTextureNodeSizeMeters();			// in meters

		if(!iTexSectorSize)
		{
			assert(0);						// maybe we should visualized this to the user
			return;								// you need to calculated the surface texture first
		}

		int iMinSecX = (int)floor(vMin.x/(float)iTexSectorSize);
		int iMinSecY = (int)floor(vMin.y/(float)iTexSectorSize);
		int iMaxSecX = (int)ceil (vMax.x/(float)iTexSectorSize);
		int iMaxSecY = (int)ceil (vMax.y/(float)iTexSectorSize);

		iMinSecX = max(iMinSecX,0);
		iMinSecY = max(iMinSecY,0);
		iMaxSecX = min(iMaxSecX,iTerrainSize/iTexSectorSize);
		iMaxSecY = min(iMaxSecY,iTerrainSize/iTexSectorSize);

		float fTerrainSize = (float)m_3DEngine->GetTerrainSize();																			// in m

		// update rectangle in 0..1 range
		float fGlobalMinX=vMin.x/fTerrainSize, fGlobalMinY=vMin.y/fTerrainSize;
		float fGlobalMaxX=vMax.x/fTerrainSize, fGlobalMaxY=vMax.y/fTerrainSize;

		for(int iY=iMinSecY; iY<iMaxSecY; ++iY)
		for(int iX=iMinSecX; iX<iMaxSecX; ++iX)
		{
			if(CTerrainManager::GetTerrainManager().IsNewTerranTextureSystem())
				m_heightmap->UpdateSectorTexture( CPoint(iY,iX),fGlobalMinY,fGlobalMinX,fGlobalMaxY,fGlobalMaxX );
		}
	}


	//////////////////////////////////////////////////////////////////////////
	// Update surface types.
	//////////////////////////////////////////////////////////////////////////
	// Build rectangle in heightmap coordinates.
	{
		float fTerrainSize = (float)m_3DEngine->GetTerrainSize();																			// in m
		int hmapWidth = m_heightmap->GetWidth();
		int hmapHeight = m_heightmap->GetHeight();

		float fX=m_pointerPos.y*hmapWidth/fTerrainSize;
		float fY=m_pointerPos.x*hmapHeight/fTerrainSize;

		int unitSize = m_heightmap->GetUnitSize();
		float fHMRadius = m_brush.radius/unitSize;
		CRect hmaprc;

		// clip against heightmap borders
		hmaprc.left = max((int)floor(fX-fHMRadius),0);
		hmaprc.top = max((int)floor(fY-fHMRadius),0);
		hmaprc.right = min((int)ceil(fX+fHMRadius),hmapWidth);
		hmaprc.bottom = min((int)ceil(fY+fHMRadius),hmapHeight);

		// Calculate surface type for this block.
		if(!CTerrainManager::GetTerrainManager().IsNewTerranTextureSystem())
			m_heightmap->CalcSurfaceTypes( &hmaprc );

		// Update surface types at 3d engine terrain.
		m_heightmap->UpdateEngineTerrain( hmaprc.left,hmaprc.top,hmaprc.Width(),hmaprc.Height(),false,true );
	}

	GetIEditor()->GetDocument()->SetModifiedFlag(TRUE);
	GetIEditor()->SetModifiedModule(eModifiedTerrain);
	GetIEditor()->UpdateViews(eUpdateHeightmap);

	bPaintLock = false;
}

//////////////////////////////////////////////////////////////////////////
CLayer* CTerrainTexturePainter::GetSelectedLayer() const
{
	//CString selLayer = s_toolPanel->GetSelectedLayer();
	CLayer* pSelLayer = s_toolPanel->GetSelectedLayer();
	CCryEditDoc *pDoc = GetIEditor()->GetDocument();
	for (int i = 0; i < CTerrainManager::GetTerrainManager().GetLayerCount(); i++)
	{
		CLayer *pLayer = CTerrainManager::GetTerrainManager().GetLayer(i);
		if (pSelLayer == pLayer)
		{
			return pLayer;
		}
	}
	return 0;
}

/*
//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::ImportExport(bool bIsImport, bool bIsClipboard)
{
	if(!bIsClipboard)
	{
		if(!strlen(gSettings.terrainTextureExport))
			gSettings.BrowseTerrainTexture();

		if(!strlen(gSettings.terrainTextureExport))
		{
			AfxMessageBox("Error: Need to specify file name.");
			return;
		}
	}

	BBox box;
	GetIEditor()->GetSelectedRegion( box );

	CPoint minp;
	CPoint maxp;

	if(box.IsEmpty())
	{
		minp = CPoint(0, 0);
		maxp = CPoint(m_heightmap->GetWidth(), m_heightmap->GetHeight());
	}
	else
	{
		minp = m_heightmap->WorldToHmap( box.min );
		maxp = m_heightmap->WorldToHmap( box.max );
	}


	m_heightmap->m_TerrainRGBTexture.ImportExportBlock( bIsClipboard ? 0 : gSettings.terrainTextureExport,
		(float)minp.x/m_heightmap->GetWidth(), (float)minp.y/m_heightmap->GetHeight(), 
		(float)maxp.x/m_heightmap->GetWidth(), (float)maxp.y/m_heightmap->GetHeight(), bIsImport);

	if(bIsImport)
	{
		RECT rc = {minp.x, minp.y, maxp.x, maxp.y};
		m_heightmap->UpdateLayerTexture(rc);
	}
}
*/

//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::Action_StartUndo()
{
	if(m_bIsPainting )
		return;

	m_pTPElem = new CUndoTPElement;

	m_bIsPainting = true;
}

//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::Action_CollectUndo(float x, float y, float radius)
{
	if(!m_bIsPainting )
		Action_StartUndo();

	m_pTPElem->AddSector(x, y, radius);
}



//////////////////////////////////////////////////////////////////////////
void CTerrainTexturePainter::Action_StopUndo()
{
	if(!m_bIsPainting)
		return;

	GetIEditor()->BeginUndo();

	if (CUndo::IsRecording())
		CUndo::Record( new CUndoTexturePainter(this) );

	GetIEditor()->AcceptUndo("Texture Layer Painting");

	m_bIsPainting = false;
}